using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using Microsoft.Xna.Framework.Graphics;

namespace XNASpriteLib
{
	public class Sprite
	{
		private readonly Frame[,,][] frame;  // [set,animation,orientation][frameIndex]

		private readonly List<Set> set = new List<Set>();
		private readonly List<Animation> animation = new List<Animation>();
		private readonly List<Orientation> orientation = new List<Orientation>();

		private readonly Dictionary<String, int> setLookup = new Dictionary<string, int>();
		private readonly Dictionary<String, int> animationLookup = new Dictionary<string, int>();
		private readonly Dictionary<String, int> orientationLookup = new Dictionary<string, int>();


		public Sprite(Stream spriteFile, GraphicsDevice graphicsDevice)
		{
			Dictionary<String, Texture2D> textureDictionary = new Dictionary<string, Texture2D>(10);

			XmlDocument doc = new XmlDocument();
			doc.Load(spriteFile);

			XmlElement root = doc["sprite"];
			if(root == null)
			{
				throw new Exception("Not a sprite file");
			}

			// First get a count of everything
			XmlNode currNode = root.FirstChild;
			int setCount = 0;
			int animationCount = 0;
			int orientationCount = 0;

			while (currNode != null)
			{

				switch (currNode.Name.ToLower())
				{
				case "set":
					Set newSet = new Set(currNode);
					this.set.Add(newSet);
					setLookup.Add(newSet.Name, setCount);
					setCount++;

					break;
				case "animation":
					Animation newAnim = new Animation(currNode, 10.0);
					this.animation.Add(newAnim);
					animationLookup.Add(newAnim.Name, animationCount);
					animationCount++;

					break;
				case "orientation":
					Orientation newOrientation = new Orientation(currNode);
					this.orientation.Add(newOrientation);
					orientationLookup.Add(newOrientation.Name, orientationCount);
					orientationCount++;

					break;
				case "sequence":

					if(frame == null)
					{
						frame = new Frame[setCount, animationCount, orientationCount][];
					}

					String setName = currNode.Attributes["set"].Value;
					String animationName = currNode.Attributes["animation"].Value;
					String orientationName = currNode.Attributes["orientation"].Value;

					int frameCount = 0;
					XmlNode frameNode = currNode.FirstChild;
					while (frameNode != null)
					{
						frameCount++;
						frameNode = frameNode.NextSibling;
					}

					frame[setLookup[setName], animationLookup[animationName], orientationLookup[orientationName]] = new Frame[frameCount];


					// Reset the frame node to the start of the sequence
					frameNode = currNode.FirstChild;
					int currFrame = 0;
					while(frameNode != null)
					{
						Frame newFrame = new Frame(frameNode, graphicsDevice, textureDictionary);
						frame[setLookup[setName], animationLookup[animationName], orientationLookup[orientationName]][currFrame] = newFrame;
						currFrame++;

						frameNode = frameNode.NextSibling;
					}

					break;
				default:
					// do nothing, invalid tag
					break;
				}

				currNode = currNode.NextSibling;
			}

		}


		internal List<Set> Set
		{
			get
			{
				return set;
			}
		}

		public Dictionary<String, int> SetLookup
		{
			get
			{
				return setLookup;
			}
		}


		internal List<Animation> Animation
		{
			get
			{
				return animation;
			}
		}

		public Dictionary<String, int> AnimationLookup
		{
			get
			{
				return animationLookup;
			}
		}


		internal List<Orientation> Orientation
		{
			get
			{
				return orientation;
			}
		}

		public Dictionary<String, int> OrientationLookup
		{
			get
			{
				return orientationLookup;
			}
		}

		public Frame[,,][] Frame
		{
			get
			{
				return frame;
			}
		}
	}
}
